let pomelo = require("pomelo");
let ZhuojiGameConstant = require("./constant/zhuojiGameConstant");
let DateUtil = require("../../utils/dateUtil");
let MahjongCheckResult = require("./domain/mahjongCheckResult");
let logger = require("pomelo-logger").getLogger(__filename);
let ZhuojiMahjongProcessor = require("./zhuojiMahjongProcessor");
let ZhuojiNotifyActiveMsg = require("./msg/zhuojiNotifyActiveMsg");
let RobotMsg = require("./robot/robotMsg");
let Random = require("../../utils/random");

const MAX_WAITING_SHOW_INIT_CARD_TIME = 1 * 1000;
const GAME_OVER_WAITING_START_GAME_TIME = 100;
const OPERATION_OVER_TIME = 16 * 1000;
const MAX_WATING_TIME = 3 * 1000;
const ENABLE_ROBOT = 1;

let ZhuojiGameLogicProcessor = function () {
    
};

ZhuojiGameLogicProcessor.prototype.processWatingPlayerState = function (gt) {
    if(gt.isReady()) {
        gt.gameStart();
    } else {
        if (ENABLE_ROBOT === 1) {
            if (gt.isAllRobot()) {
                for (let robot of gt.getAllPlayers()) {
                    pomelo.app.get("zhuojiRobotService").kickRobot(gt, robot);
                    gt.leave(robot);
                }
                gt.reset();
            } else {
                let delta = DateUtil.nowMS() - gt.getLastEnterTime();
                if (delta > Random.randRange(6000, 12000) && !gt.isFull() && !gt.isEmpty() && !gt.isAllRobot()) {
                    // if (delta > MAX_WATING_TIME && !gt.isFull()) {
                    let robot = pomelo.app.get("zhuojiRobotService").getRobot(gt);
                    if (robot != null) {
                        gt.join(robot);
                        setTimeout( () => {
                            gt.setReady(robot);
                            gt.broadCast("onPlayerGotReady", {uid: robot.getUid()});
                        }, Random.randRange(2000, 4000));
                    }
                }
                gt.checkPlayerTimeout();
            }
        } else {
            gt.checkPlayerTimeout();
        }

    }


};

ZhuojiGameLogicProcessor.prototype.playingTableTick = function (gt) {
    let now = DateUtil.nowMS();
    let subState = gt.getPlaySubstate();
    let currentPlayer = null;

    let waitingPlayerOperateResult = gt.getWaitingPlayerOperate();
    // 如果有等待操作，就让对方操作;如果没有等待玩家，就是当前按位置的玩家
    if (waitingPlayerOperateResult != null) {
        currentPlayer = gt.getPlayerByUid(waitingPlayerOperateResult.uid);
    } else {
        currentPlayer = gt.getCurrentOperationPlayer();
    }
    if (currentPlayer == null) {
        logger.info(`playingTableTick currentPlayer = null`);
        return;
    } else if (subState === ZhuojiGameConstant.GAME_TABLE_SUB_STATE_WAITING_CLIENT_SHOW_INIT_CARDS) {
        let delta = now - gt.getReadyTime();
        if (delta > MAX_WAITING_SHOW_INIT_CARD_TIME) {
            gt.notifyActivePlayer(true);
        }
    } else if (subState === ZhuojiGameConstant.GAME_TABLE_SUB_STATE_IDLE) {
        // 普通房间，如果玩家时托管状态，则直接帮他操作，否则等他超时
        // 防止超时时候，客户端进行出牌操作
        if (currentPlayer.autoOperation === 1 || (now - currentPlayer.opStartTime >= OPERATION_OVER_TIME)) {
            currentPlayer.autoOperation = 1;
            this.processOverTime(currentPlayer, gt);
        }
    } else if (subState === ZhuojiGameConstant.GAME_TABLE_SUB_STATE_AUTO_MO_CARD) {
        currentPlayer.push("onZhuojiNotifyActive", {
            uid : currentPlayer.getUid(),
            operation : ZhuojiGameConstant.MAHJONG_OPERTAION_CHU,
            grabCard : currentPlayer.cardGrab,
        });
        gt.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_IDLE);
        this.playerChuOperation(gt, currentPlayer, currentPlayer.cardGrab);
    // } else if (subState === ZhuojiGameConstant.GAME_TABLE_SUB_STATE_SHOW_GAME_OVER_SCREEN) {
    //     let delta = now - gt.getHandEndTime();
    //     if (delta > GAME_OVER_WAITING_START_GAME_TIME) {
    //         gt.gameFinish();
    //     }
    }
}


/**
 * 玩家出牌操作
 */
ZhuojiGameLogicProcessor.prototype.playerChuOperation = function (gt, player, card) {
    let currentOperationPlayer = gt.getCurrentOperationPlayer();
    if(currentOperationPlayer == null) {
        logger.error(`table_pos 当前没轮到此玩家操作=${player.getUid()}, tablePos=${player.getPosition()}, tableID=${gt.getTid()}`);
        player.errorTime ++;
        return;
    }
    if (currentOperationPlayer.getUid() !== player.getUid()) {
        logger.error(`table_pos 当前没轮到此玩家操作=${player.getUid()}, tablePos=${player.getPosition()}, 
        currentOpUid=${currentOperationPlayer.getUid()}, tableID=${gt.getTid()}`);
        player.errorTime ++;
        return;
    }
    if (gt.getPlaySubstate() !== ZhuojiGameConstant.GAME_TABLE_SUB_STATE_IDLE) {
        logger.error(`sub state not idle 当前没轮到此玩家操作=${player.getUid()}, tablePos=${player.getPosition()}`);
        player.errorTime ++;
        return;
    }
    if (!player.hasCardInHand(card)) {
        logger.error(`--ZhuojiGameLogicProcessor playerChuOperation end-- 出牌错误,playerUid=${player.getUid()},card=${card.toString(16)},
        player.getPosition()=${player.getPosition()},tableID=${gt.getTid()}`);
    }
    // 看看之前刚摸的牌有没有，摸的牌，先放cardgrab变量里面，等他出牌的时候再放进去，这样断线重连的时候，他摸的还是原来那张
    if (player.cardGrab !== 0) {
        player.addCardInHand(player.cardGrab);
        player.clearCardGrab();
    }
    // 如果玩家手中的牌不够，先让他摸一张
    if (player.cardsInHand.length % 3 != 2) {
        logger.error(`玩家手中的牌不够,player:${player}`);
        this.grabCard(gt);
        player.errorTime ++;
        return;
    }

    // gt.setChuPlayer(player);// 记录出牌的人
    player.chuPaiNum ++;// 出牌次数加1
    gt.addCardToCardPool(card);
    player.removeCardInHand(card);
    // 清除胡牌 碰牌
    player.clearLastCancelHuCard();
    player.clearLastCancelPengCard();


    if (gt.isChongfeng(card)) {
        gt.jiPositions.chongFeng = player.position;
        logger.info(`玩家打出冲锋鸡！`);
    }
    if (gt.wuguji && gt.isChongfengWugu(card)) {
        gt.jiPositions.chongFengWugu = player.position;
        logger.info(`玩家打出冲锋乌骨鸡！`);
    }

    printOpeartionLog(ZhuojiGameConstant.MAHJONG_OPERTAION_CHU, card, player, gt);


    //点听完 第一次出的牌 把扣牌状态置为true 只有按钮听有的时候
    if (player.onclickTing && !player.onclickTingChuPai) {
        player.tingChuType = player.tingType;
        player.onclickTingChuPai = true;
    }
    // 把牌放在桌子中间，如果没有吃碰胡之类，牌就放在这个玩家面前
    gt.setChu(player.getUid(), player.getPosition(), card);
    // gt.setCurrentCard(card);
    // gt.setCardOpPlayerPosition(player.getPosition());
    gt.broadCast("onZhuojiOperateResult",{
        uid : player.getUid(),
        operation : ZhuojiGameConstant.MAHJONG_OPERTAION_CHU,
        card : card,
        jiPositions : [gt.jiPositions.chongFeng, gt.jiPositions.zeRen, gt.jiPositions.chongFengWugu, gt.jiPositions.zeRenWugu],
        tingChuType : player.tingChuType
    });

    // 服务器清除等待玩家操作的数据
    gt.setWaitingPlayerOperate(null);
    gt.setLastChuAction();

    gt.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_IDLE);
    this.notifyNextPlayerOperation(gt);
};

/**
 * 玩家碰牌操作
 */
ZhuojiGameLogicProcessor.prototype.player_op_peng = function (gt, player) {
    // 玩家出牌
    let waiting = gt.getWaitingPlayerOperate();
    logger.info(`player_op_peng waiting : ${waiting.toString()}`);
    let card = waiting.targetCard;
    if (waiting == null) {
        logger.error(`peng,no such operation, playerUid=${player.getUid()}, tableID=${gt.getTid()}`);
        return;
    }
    if (waiting.uid !== player.getUid()) {
        logger.error(`peng,table position invalid, playerUid=${player.getUid()}, tableID=${gt.getTid()}`);
        player.errorTime ++;
        return;
    }
    if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) !== ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) {
        logger.error(`peng,operation invalid, playerUid=${player.getUid()}, tableID=${gt.getTid()}`);
        player.errorTime ++;
        return;
    }
    if (player.haveCancelPengCards(card)) {
        logger.error(`不能连碰, playerUid=${player.getUid()}, tableID=${gt.getTid()}`);
        return;
    }
    printOpeartionLog(ZhuojiGameConstant.MAHJONG_OPERTAION_PENG, card, player, gt);

    player.clearLastCancelHuCard();
    player.clearLastCancelPengCard();
    // 去掉可能的被抢杠前的记录
    gt.setGangOpBackup(null);

    // 这时候要把当前操作的索引改成此玩家，这样他出牌的时候，当前操作玩家的索引才是正确的
    gt.setCurrentPosition(player.getPosition());

    gt.addCardsToCardPool([card, card]);
    player.removeCardInHand(card);
    player.removeCardInHand(card);

    if (gt.isZeRen(card, ZhuojiGameConstant.MAHJONG_OPERTAION_PENG)) {
        gt.jiPositions.zeRen = player.getPosition();
        logger.info(`玩家打出责任鸡！`);
    }
    if (gt.wuguji && gt.isZeRenWugu(card, ZhuojiGameConstant.MAHJONG_OPERTAION_PENG)) {
        gt.jiPositions.zeRenWugu = player.getPosition();
        logger.info(`玩家打出责任乌骨鸡！`);
    }
    let down = player.addCardDown(card, ZhuojiGameConstant.MAHJONG_OPERTAION_PENG, gt.chu.uid);

    gt.broadCast("onZhuojiOperateResult",{
        uid : player.getUid(),
        operation : ZhuojiGameConstant.MAHJONG_OPERTAION_PENG,
        card : card,
        jiPositions : [gt.jiPositions.chongFeng, gt.jiPositions.zeRen, gt.jiPositions.chongFengWugu, gt.jiPositions.zeRenWugu]
    });

    // 服务器清除等待玩家操作的数据
    gt.setWaitingPlayerOperate(null);
    gt.clearChu()
    // gt.setCurrentCard(0);
    // gt.setCardOpPlayerPosition(-1);
    gt.setLastPengAction();
    // 当碰完后,不能立刻胡牌,通知玩家出牌
    // gt.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_IDLE);
    gt.notifyActivePlayer(false);
};

/**
 * 玩家杠牌操作
 */
ZhuojiGameLogicProcessor.prototype.player_op_gang = function (gt, player, card) {
    let waiting = gt.getWaitingPlayerOperate();
    logger.info(` player_op_gang : ${waiting.toString()}`);
    if (waiting == null) {
        logger.error(`--player_op_gang--,no gang operation, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        return;// 当前没有在等待某个玩家操作；
    }
    if (waiting.uid !== player.getUid()) {
        logger.error(`--player_op_gang-- table position invalid, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        return;// 当前不是在等这个玩家操作
    }
    if (waiting.hasGangAndSetOperation(card) === false) {
        logger.error(`gang,parameter invalid, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        return;// 杠的牌不对；
    }

    // 哪种形式的杠
    let gangType = 0;
    if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG) {
        logger.info(`gang,mingGang, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        gangType = ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG;
    } else {
        if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG) {
            logger.info(`gang, anGang, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
            gangType = ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG;
        } else if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) {
            logger.info(`gang, buGang, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
            gangType = ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG;
        }
        // 暗杠 补杠的时候是刚摸牌，把摸的放进来
        let cardGrab = player.cardGrab;
        if (cardGrab !== 0) {
            player.addCardInHand(cardGrab);
            player.clearCardGrab();
        }
    }

    if (gangType === 0) {
        logger.error(`GANG,operation invalid,playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        return;
    }
    printOpeartionLog(ZhuojiGameConstant.MAHJONG_OPERTAION_GANG, card, player, gt);

    player.clearLastCancelHuCard();
    player.clearLastCancelPengCard();

    // 把牌放在桌子中间，如果没有吃碰胡之类，牌就放在这个玩家面前
    // gt.setChu(player.getUid(), player.getPosition(), card);
    // gt.setCurrentCard(card);
    // gt.setCardOpPlayerPosition(player.getPosition());
    //TODO: ensure
    // gt.broadCastGangNotify(player, card);

    // 补杠可以抢胡. 暗杠,明杠不能抢.
    if (gangType === ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) {
        gt.setLastAction(ZhuojiGameConstant.ACTION_BU_GANG);
        // 按优先级，先胡
        let chu = {uid: player.getUid(), position: player.getPosition(), card: card};
        let huResult = this.table_loop_check(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_HU, chu.position);
        if (huResult != null) {
            // 抢杠前先备份数据，不抢可以恢复再杠
            waiting.gangOpPlayer = player;
            gt.setGangOpBackup(waiting);
            // 可能被抢，先移走，等下如果对方不抢杠胡再放回来
            player.removeCardInHand(card);

            let huPlayer = gt.getPlayerByUid(huResult.uid);

            huResult.chuPlayer = player;// 记录被抢杠的人
            //TODO fanResult
            // huResult.fanResult |= ZhuojiGameConstant.MAHJONG_HU_CODE_QIANG_GANG_HU;// 抢杠胡
            gt.setWaitingPlayerOperate(huResult);
            huPlayer.opStartTime = DateUtil.nowMS();
            // huPlayer.sendHuOperation(gt);
            let msg = {
                uid : huPlayer.getUid(),
                operation : ZhuojiGameConstant.MAHJONG_OPERTAION_HU,
                card : card,
            };

            huPlayer.push("onZhuojiNotifyActive", msg);
                // ZhuojiRobotService.handleRobotMsg(new RobotMsg(gt, huPlayer, msg));
            if (huPlayer.isRobot === 1) {
                pomelo.app.get("zhuojiRobotService").pushMsg(new RobotMsg(gt, huPlayer, msg, DateUtil.nowMS()));
            }
        } else {
            real_player_op_gang(gt, player, card);
        }
    } else {
        real_player_op_gang(gt, player, card);
    }
};

/**
 * 玩家点胡
 */
ZhuojiGameLogicProcessor.prototype.player_op_hu = function (gt, player) {
    logger.info(`玩家选择胡牌, 桌子：${gt.getTid()}, 玩家:${player.getUid()}`);

    let waiting = gt.getWaitingPlayerOperate();
    if (waiting == null) {
        logger.error(`no such operation, PlayerUid=${player.getUid()}`);
        return;
    }
    if (waiting.uid !== player.getUid()) {
        logger.error(`player_op_hu,table position invalid, PlayerUid=${player.getUid()}`);
        return;
    }
    if (((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_HU) != ZhuojiGameConstant.MAHJONG_OPERTAION_HU)) {
        logger.error(`hu operation invalid`);
        return;
    }

    let hu_xx = ZhuojiMahjongProcessor.checkWin(waiting.targetCard, player, gt);
    if (hu_xx == null) {
        logger.error(`playerUid=${player.getUid()}, hu operation failed,card=${waiting.targetCard }, tableID=${gt.getTid()}`);
        return;
    }
    //TODO fanResult
    // if ((waiting.operation & ZhuojiGameConstant.MAHJONG_HU_GANG_SHANG_KAI_HUA) == ZhuojiGameConstant.MAHJONG_HU_GANG_SHANG_KAI_HUA) {
    //     hu_xx.fanResult |= ZhuojiGameConstant.MAHJONG_HU_GANG_SHANG_KAI_HUA;
    // }

    let huPlayers = [player];
    //check 一炮多响
    if (waiting.chuPlayer != null) {
        let position = player.getPosition();
        for (let i = position + 1; i < position + gt.getNumOfPlayers(); i++) {
            let pl = gt.getPlayerByTablePos(i % gt.getNumOfPlayers());
            if (pl.getUid() === waiting.chuPlayer.getUid() || pl.getUid() === player.getUid()) {
                break;
            }
            let result = ZhuojiMahjongProcessor.checkWin(waiting.targetCard, pl, gt);
            if (result != null) {
                huPlayers.push(pl);
            }
        }
    }

    //TODO
    // for(let player of huPlayers){
    //     player.playerHu(waiting.targetCard, hu_xx.fanResult, 0);
    // }
    gt.setCurrentHuPlayer(player);
    gt.setFanpaiJiCard();
    let lastActor = waiting.chuPlayer == null ? player : waiting.chuPlayer;
    // gt.setLastDianPaoPlayerIndex(huPlayers, lastActor);
    // gt.setLastHuPlayersByPlayers(huPlayers);
    // gt.calculateLianzhuangNum(gt.playType);
    //TODO
    // gt.resetChongfengWhileHuByOthers(huPlayers, player, waiting.targetCard);
    // gt.resetChongfengWuguWhileHuByOthers(huPlayers, player, waiting.targetCard);


    let results = gt.gameSettlement(huPlayers, lastActor, waiting.targetCard, gt.getActionType());
    gt.broadCast("onZhuojiFinish", {results : results, fanPaiJi : gt.getFanPaiJiCard(), resultType : gt.resultType, huCard : waiting.targetCard});
    gt.setHandEndTime(DateUtil.nowMS());
    // gt.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_SHOW_GAME_OVER_SCREEN);
    gt.gameFinish(results);
};

/**
 * 玩家点听
 */
ZhuojiGameLogicProcessor.prototype.player_op_ting = function (gt, player) {
    let waiting = gt.getWaitingPlayerOperate();
    if (waiting == null) {
        logger.error(`ting,no such operation, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        return;
    }
    if (waiting.uid !== player.getUid()) {
        logger.error(`ting,table position invalid, playerUid=${player.getUid()},tableID=${gt.getTid()}`);
        return;
    }

    player.setTingTypeWhileOpTing(gt.getDealerUid());

    // let tingResult = ZhuojiMahjongProcessor.checkTing(player, player.cardGrab);
    player.onclickTing = true;
    // player.sendTingChuOperation(gt, res.tingList);
    player.push("onZhuojiOperateResult", {
        uid : player.getUid(),
        operation : ZhuojiGameConstant.MAHJONG_OPERTAION_TING,
        jiPositions : [gt.jiPositions.chongFeng, gt.jiPositions.zeRen, gt.jiPositions.chongFengWugu, gt.jiPositions.zeRenWugu]
    });
    player.push("onZhuojiNotifyActive", {
        uid : player.getUid(),
        operation : ZhuojiGameConstant.MAHJONG_OPERTAION_TING_CHU,
        tingResults : gt.getTing14Hint(player, player.cardGrab)
    });
};


/**
 * 有玩家打了一张牌，现在看看下一个玩家怎么行动，可能是胡，杠，碰
 */
ZhuojiGameLogicProcessor.prototype.notifyNextPlayerOperation = function(gt) {
    let chu = gt.getChu();
    // let card = gt.getCurrentCard();
    // let chuPlayer = gt.getPlayerByTablePos(gt.getCardOpPlayerPosition());
    gt.moveToNextPlayer();
    // 流局了
    if (gt.isEnd()) {
        this.checkNextPlayerOperation(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_HU);
    } else {
        // 可以碰，杠， 点炮
        let checkOperations = ZhuojiGameConstant.MAHJONG_OPERTAION_PENG
            | ZhuojiGameConstant.MAHJONG_OPERTAION_GANG
            | ZhuojiGameConstant.MAHJONG_OPERTAION_HU;
        this.checkNextPlayerOperation(gt, chu, checkOperations);
    }
};

ZhuojiGameLogicProcessor.prototype.checkNextPlayerOperation = function(gt, chu, checkOperations) {
    if (chu.position === -1) {
        logger.error(`--ZhuojiGameLogicProcessor checkNextPlayerOperation-- tableID:${gt.getTid()},chu:${JSON.stringify(chu)}`);
        return;
    }
    let result = null;
    let msg = new ZhuojiNotifyActiveMsg();
    do {
        // 1先看有没有胡的玩家
        if ((checkOperations & ZhuojiGameConstant.MAHJONG_OPERTAION_HU) == ZhuojiGameConstant.MAHJONG_OPERTAION_HU) {
            result = this.table_loop_check(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_HU, chu.position);
            if (result != null) {
                // int waitFan = getWaitFan(gt);
                // result.addFanResult(waitFan);
                // 平胡不能点炮
                // if (result != null) {
                    result.chuPlayer = gt.getPlayerByUid(chu.uid);// 这个是点炮的玩家
                    msg.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_HU;
                    msg.card = result.targetCard;
                    break;
                // }
            }
        }
        // 2看有没有玩家杠
        if ((checkOperations & ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) == ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) {
            result = this.table_loop_check(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_GANG, chu.position);
            if (result != null) {
                result.chuPlayer = gt.getPlayerByUid(chu.uid);
                msg.operation |= result.operation;
                msg.gangList = result.gangList;
                // 杠的同时，肯定可以碰
                let pengResult = this.table_loop_check(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_PENG, chu.position);

                if (pengResult != null) {
                    msg.operation |= ZhuojiGameConstant.MAHJONG_OPERTAION_PENG;
                }
                break;
            }
        }
        // 3看有没有玩家可以碰
        if ((checkOperations & ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) == ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) {
            result = this.table_loop_check(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_PENG, chu.position);
            if (result != null) {
                msg.operation |= ZhuojiGameConstant.MAHJONG_OPERTAION_PENG;
                break;
            }
        }
    } while (false);

    if (result != null) {
        result.operation = msg.operation;// 以msg的操作为准；
        gt.setWaitingPlayerOperate(result);

        msg.uid = result.uid;
        msg.card = result.targetCard;
        let pl = gt.getPlayerByUid(msg.uid);
        pl.opStartTime = DateUtil.nowMS();

        pl.push("onZhuojiNotifyActive", msg.toJSON());
        if (pl.isRobot === 1) {
            // ZhuojiRobotService.handleRobotMsg(new RobotMsg(gt, pl, msg.toJSON()));
            pomelo.app.get("zhuojiRobotService").pushMsg(new RobotMsg(gt, pl, msg.toJSON(), DateUtil.nowMS()));
        }
    } else {
        this.grabCard(gt);
    }
};

/**
 * 按顺序查询下玩家能否进行某个操作
 * chuCardPlayerTablePosition，一个人出牌，引发多人吃，每次检测到了出牌那个位置，肯定要停止了，不能无限循环
 */
ZhuojiGameLogicProcessor.prototype.table_loop_check = function(gt, chu, checkOperation, fromPosition) {
    let mahjongCheckResult = null;
    let players = gt.getAllPlayers();

    let nextPlayerPosition = fromPosition + 1;
    if (nextPlayerPosition >= players.length) {
        nextPlayerPosition = 0;
    }

    for (let i = 0; i < players.length - 1; i++) {
        let player = players[nextPlayerPosition];
        if (nextPlayerPosition === chu.position) {
            break;
        }
        // 已经听牌，看看能胡不
        if (checkOperation == ZhuojiGameConstant.MAHJONG_OPERTAION_HU) {
            // 先检测卡胡
            let huType = player.calculateHuType(chu.card);
            let extraHuType = player.tingChuType || gt.getPlayerByUid(chu.uid).tingChuType;
            if(gt.getLastAction() === ZhuojiGameConstant.ACTION_MING_GANG_CHU){
                extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_MING_GANG_PAO;
            } else if (gt.getLastAction() === ZhuojiGameConstant.ACTION_AN_GANG_CHU){
                extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_AN_GANG_PAO;
            } else if (gt.getLastAction() === ZhuojiGameConstant.ACTION_BU_GANG_CHU){
                extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_BU_GANG_PAO;
            } else if (gt.getLastAction() === ZhuojiGameConstant.ACTION_BU_GANG){
                extraHuType = ZhuojiGameConstant.MAHJONG_HU_CODE_QIANG_GANG_HU;
            }
            if (huType !== ZhuojiGameConstant.HU_TYPE_PING_HU ||  player.hasDou() || extraHuType !== 0) {
                mahjongCheckResult = ZhuojiMahjongProcessor.checkWin(chu.card, player, gt);
            }
            //TODO fanResult
            // if (mahjongCheckResult != null) {
            //     let waitFan = getWaitFan(gt);
            //     mahjongCheckResult.addFanResult(waitFan);
            // }
        }
        if (checkOperation === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) {
            mahjongCheckResult = ZhuojiMahjongProcessor.checkGang(gt, chu.card, player, false, chu.uid);
        }
        if (checkOperation === ZhuojiGameConstant.MAHJONG_OPERTAION_PENG){
            mahjongCheckResult = ZhuojiMahjongProcessor.checkPeng(chu.card, player);
        }
        if (mahjongCheckResult != null) {
            break;
        }
        nextPlayerPosition ++;
        if (nextPlayerPosition >= players.length){
            nextPlayerPosition = 0;
        }
    }
    return mahjongCheckResult;
};


ZhuojiGameLogicProcessor.prototype.grabCard = function(gt) {
    let currentOperationPlayer = gt.getCurrentOperationPlayer();
    if (currentOperationPlayer == null) {
        logger.error(`玩家摸牌失败,当前操作玩家不存在,tableId:${gt.getTid()}`);
        return;
    }
    logger.info(`---grabCard--- playerUid:${currentOperationPlayer.getUid()},tableID:${gt.getTid()}`);
    // gt.broadCast("onZhuojiTip", {uid : currentOperationPlayer.getUid()});

    currentOperationPlayer.clearLastCancelHuCard();
    // 去掉可能的被抢杠前的记录
    gt.setGangOpBackup(null);

    let chu = gt.getChu();
    // let card = gt.getCurrentCard();
    if (chu.card !== 0) {
        let pl = gt.getPlayerByTablePos(chu.position);
        pl.addCardBefore(chu.card);
        gt.clearChu();
        // gt.setCurrentCard(0);
        // gt.setCardOpPlayerPosition(-1);
    }

    gt.setWaitingPlayerOperate(null);
    // 摸光为止
    if (gt.isEnd()) {
        let results = gt.gameSettlement([], currentOperationPlayer, 0, 1);
        gt.broadCast("onZhuojiFinish", {results : results, resultType : gt.resultType});
        gt.setHandEndTime(DateUtil.nowMS());
        // gt.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_SHOW_GAME_OVER_SCREEN);
        gt.gameFinish(results);
    } else {
        if (currentOperationPlayer.cardsInHand.length % 3 === 1 && currentOperationPlayer.cardGrab === 0) {
            let cardGrab = gt.popFirstCard();
            currentOperationPlayer.setCardGrab(cardGrab, 1);
            currentOperationPlayer.moCardNum ++;
        }
        gt.notifyActivePlayer(true);
    }
};

ZhuojiGameLogicProcessor.prototype.player_cancel_chi_peng_gang_hu = function (gt, pl) {
    let waiting = gt.getWaitingPlayerOperate();
    if (waiting == null) {
        logger.error(`player_cancel_chi_peng_gang_hu waiting is null. playerUid=${pl.getUid()}`);
        return;
    }
    if (waiting.uid !== pl.getUid()) {
        logger.error(`player_cancel_chi_peng_gang,table position invalid. playerUid=${pl.getUid()}, tableID=${gt.getTid()}`);
        pl.errorTime ++;
        return;
    }
    if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_TING) === ZhuojiGameConstant.MAHJONG_OPERTAION_TING) {
        if (((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG)
            && gt.getCurrentOperationPlayer().getUid() === pl.getUid()) {
            wait_player_gang(gt, waiting, pl);
        } else {
            wait_player_chu(gt, waiting, pl);
        }
        return;
    }

    // 自己摸起来暗杠或者补杠，玩家取消
    if (((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG)
        && gt.getCurrentOperationPlayer().getUid() === pl.getUid()) {
        if (pl.cardGrab === 0 && ((pl.cardsInHand.length % 3) === 1)) {
            this.grabCard(gt);
        } else {
            wait_player_chu(gt, waiting, pl);
        }
        return;
    }
    printOpeartionLog(ZhuojiGameConstant.MAHJONG_OPERTAION_CANCEL, 0, pl, gt);

    // 找到出牌的那个玩家，是他引起其他玩家可以吃碰之类
    let chu = gt.getChu();
    // let chuPlayerPosition = gt.getCardOpPlayerPosition();
    // let chuPlayer = gt.getPlayerByTablePos(chuPlayerPosition);

    // 再找到按顺序，出牌的下家，应该是当前操作的玩家，如果没有吃碰
    let currentOperationPlayer = gt.getCurrentOperationPlayer();
    if (currentOperationPlayer == null){
        return;
    }
    // 不胡，这里先处理自己摸的牌不想胡，其他人打的在后面处理
    if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_HU) === ZhuojiGameConstant.MAHJONG_OPERTAION_HU) {
        // 我自己摸起来的牌，我自己不胡
        if ((chu.uid === -1 || chu.uid === pl.getUid()) && currentOperationPlayer.getUid() === pl.getUid()) {
            wait_player_chu(gt, waiting, pl);
            return;
        }
    }

    // 玩家不碰杠（被提示玩家只有碰杠的操作）
    if (((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) == ZhuojiGameConstant.MAHJONG_OPERTAION_PENG)
        || ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) == ZhuojiGameConstant.MAHJONG_OPERTAION_GANG)) {
        if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) == ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) {
            pl.addLastCancelPengCard(waiting.targetCard);
            gt.setWaitingPlayerOperate(null);
        }
        logger.info(`cancel peng/gang, mo player=${currentOperationPlayer.getUid()}, tableID=${gt.getTid()}`);
        this.grabCard(gt);
        return;
    } else if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_HU) === ZhuojiGameConstant.MAHJONG_OPERTAION_HU) {
        // 过胡
        pl.addLastCancelHuCard(waiting.targetCard);

        // 别人打的牌，我不胡，看看其他人有人胡不
        //TODO refactor
        // let hu = this.table_loop_check(gt, waiting.targetCard, pl, ZhuojiGameConstant.MAHJONG_OPERTAION_HU, chuPlayerPosition);
        let hu = this.table_loop_check(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_HU, pl.getPosition());
        if (hu != null) {
            // let waitFan = getWaitFan(gt);
            // hu.addFanResult(waitFan);
            hu.chuPlayer = gt.getPlayerByUid(chu.uid);
            gt.setWaitingPlayerOperate(hu);

            let huPlayer = gt.getPlayerByUid(hu.uid);
            huPlayer.opStartTime = DateUtil.nowMS();

            let msg = {
                uid : huPlayer.getUid(),
                operation : ZhuojiGameConstant.MAHJONG_OPERTAION_HU,
                card : card,
            };

            huPlayer.push("onZhuojiNotifyActive", msg);
            if (huPlayer.isRobot === 1) {
                // ZhuojiRobotService.handleRobotMsg(new RobotMsg(gt, huPlayer, msg));
                pomelo.app.get("zhuojiRobotService").pushMsg(new RobotMsg(gt, huPlayer, msg, DateUtil.nowMS()));
            }
        } else {
            // 如果抢杠胡不胡，玩家继续杠
            let gangWaiting = gt.getGangOpBackup();
            if (gangWaiting != null && gt.getLastAction() === ZhuojiGameConstant.ACTION_BU_GANG) {
                logger.info(`抢杠不胡，继续杠, player=${currentOperationPlayer.getUid()}, tableID=${gt.getTid()}`);

                gt.setWaitingPlayerOperate(gangWaiting);
                real_player_op_gang(gt, gangWaiting.gangOpPlayer, gangWaiting.targetCard);
            } else {
                // 去掉可能的被抢杠前的记录
                gt.setGangOpBackup(null);
                logger.info(`cancel hu, player=${currentOperationPlayer.getUid()}, tableID=${gt.getTid()}`);

                // 别人点炮，你不胡，检测是否吃碰杠
                // this.checkNextPlayerOperation(gt, waiting.targetCard, chuPlayer,
                //     ZhuojiGameConstant.MAHJONG_OPERTAION_GANG | ZhuojiGameConstant.MAHJONG_OPERTAION_PENG);
                this.checkNextPlayerOperation(gt, chu, ZhuojiGameConstant.MAHJONG_OPERTAION_GANG | ZhuojiGameConstant.MAHJONG_OPERTAION_PENG);

            }
        }
    }
};


ZhuojiGameLogicProcessor.prototype.processOverTime = function (player, gt) {
    logger.info(`-----------processOverTime-------------- playerUid:${player.getUid()}`);
    gt.broadCast("onZhuojiTuoGuan", {uid : player.getUid(), auto : 1});
    let msg = new ZhuojiNotifyActiveMsg();
    let checkResult = gt.getWaitingPlayerOperate();
    if (checkResult != null) {
        let operation = checkResult.operation;
        // 自动胡
        if ((operation & ZhuojiGameConstant.MAHJONG_OPERTAION_HU) === ZhuojiGameConstant.MAHJONG_OPERTAION_HU) {
            msg.uid = player.getUid();
            msg.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_HU;
            if (checkResult.targetCard !== 0) {
                msg.card = checkResult.targetCard;
            }
            gt.player_op_hu(player, msg);
            return;
        }
        // 其余所有操作全部按过处理
        else if (((operation & ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) === ZhuojiGameConstant.MAHJONG_OPERTAION_PENG)
            || ((operation & ZhuojiGameConstant.MAHJONG_OPERTAION_TING) === ZhuojiGameConstant.MAHJONG_OPERTAION_TING)
            || ((operation & ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG)) {
            msg.uid = player.getUid();
            msg.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_CANCEL;

            player.push("onZhuojiNotifyActive", msg.toJSON());
            this.player_cancel_chi_peng_gang_hu(gt, player);
            gt.setWaitingPlayerOperate(null);

            msg.uid = player.getUid();
            msg.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_CHU;
            let cardGrab = player.getCardGrab();
            let card = cardGrab !== 0 ? cardGrab : player.cardsInHand[0];
            msg.card = card;
            player.push("onZhuojiNotifyActive", msg.toJSON());
            this.playerChuOperation(gt, player, card);
        }
    }
    //玩家点听 没打牌 托管了 状态重置
    if (player.onclickTing && !player.onclickTingChuPai) {
        player.onclickTing = false;
    }
    if(msg.operation === 0) {
        msg.uid = player.getUid();
        msg.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_CHU;
        let cardGrab = player.getCardGrab();
        let card = cardGrab !== 0 ? cardGrab : player.cardsInHand[0];
        msg.card = card;
        player.push("onZhuojiNotifyActive", msg.toJSON());
        this.playerChuOperation(gt, player, card);
    }
};

function real_player_op_gang(gt, pl, card) {
    let waiting = gt.getWaitingPlayerOperate();
    if (waiting == null) {
        logger.error(`gang,no such operation, playerUid=${pl.getUid()}, tableID=${gt.getTid()}`);
        return;
    }
    if (waiting.uid !== pl.getUid()) {
        logger.error(`gang,table position invalid, playerUid=${pl.getUid()}, tableID=${gt.getTid()}`);
        return;
    }
    // 哪种形式的杠
    let gang_type = 0;
    if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG) {
        gang_type = ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG;
    } else {
        if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG) {
            gang_type = ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG;
        } else if ((waiting.operation & ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) === ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) {
            gang_type = ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG;
        }
    }
    if (gang_type === 0) {
        logger.error(`GANG,operation invalid, playerUid=${pl.getUid()}, tableID=${gt.getTid()}`);
        return;
    }

    // 这时候要把当前操作的索引改成此玩家，这样他出牌的时候，当前操作玩家的索引才是正确的
    gt.setCurrentPosition(pl.getPosition());

    if (gang_type === ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG) {
        gt.addCardsToCardPool([card, card, card]);
        pl.removeCardInHand(card);
        pl.removeCardInHand(card);
        pl.removeCardInHand(card);
        pl.addCardDown(card, ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG, waiting.chuPlayer.getUid());

        gt.setLastAction(ZhuojiGameConstant.ACTION_MING_GANG);
    } else if (gang_type === ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG) {
        // 把摸的牌先放进来
        let moCard = pl.cardGrab;
        if (moCard !== 0) {
            pl.addCardInHand(moCard);
            pl.clearCardGrab();
        }
        gt.addCardsToCardPool([card, card, card, card]);
        pl.removeCardInHand(card);
        pl.removeCardInHand(card);
        pl.removeCardInHand(card);
        pl.removeCardInHand(card);
        pl.addGangCardDown(card, ZhuojiGameConstant.MAHJONG_OPERTAION_AN_GANG, pl);

        gt.setLastAction(ZhuojiGameConstant.ACTION_AN_GANG);
    } else if (gang_type === ZhuojiGameConstant.MAHJONG_OPERTAION_BU_GANG) {
        pl.removeCardInHand(card);
        gt.addCardToCardPool(card);
        pl.buGang(card);

        gt.setLastAction(ZhuojiGameConstant.ACTION_BU_GANG);
    }
    logger.info(`-------------gang_type:${gang_type} ${gt.cardPool.get(card)}`);
    if (gang_type === ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG && gt.isZeRen(card, ZhuojiGameConstant.MAHJONG_OPERTAION_GANG)) {
        gt.jiPositions.zeRen = pl.getPosition();
        logger.info(`玩家打出责任鸡！！！`);
    }
    if (gang_type === ZhuojiGameConstant.MAHJONG_OPERTAION_MING_GANG && gt.wuguji && gt.isZeRenWugu(card, ZhuojiGameConstant.MAHJONG_OPERTAION_GANG)) {
        gt.jiPositions.zeRenWugu = pl.getPosition();
        logger.info(`玩家打出责任乌骨鸡！！！`);
    }

    gt.broadCast("onZhuojiOperateResult",{
        uid : pl.getUid(),
        operation : gang_type,
        card : card,
        jiPositions : [gt.jiPositions.chongFeng, gt.jiPositions.zeRen, gt.jiPositions.chongFengWugu, gt.jiPositions.zeRenWugu]
    });

    // 服务器清除等待玩家操作的数据
    gt.setWaitingPlayerOperate(null);
    gt.clearChu();
    // gt.setCurrentCard(0);
    // gt.setCardOpPlayerPosition(-1);

    // 杠完摸一张
    if (pl.cardsInHand.length % 3 === 1 && pl.getCardGrab() === 0) {
        let cardGrab = gt.popLastCard();
        pl.setCardGrab(cardGrab, 2);
        pl.moCardNum++;
    }
    gt.notifyActivePlayer(true);
};

function wait_player_chu(gt, waiting, pl) {
    // let hasGrabCard = pl.grabCard === 0 ? 0 : 1;
    // gt.broadCast("onZhuojiTip", {uid : pl.getUid(), hasGrabCard : hasGrabCard});

    // 等待玩家出牌
    let chu = new MahjongCheckResult();
    chu.uid = waiting.uid;
    chu.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_CHU;
    //TODO fanResult
    // chu.addFanResult(getWaitFan(gt));
    gt.setWaitingPlayerOperate(chu);
//		如果是已经点过 听 无胡 无杠  就自动出牌
    if (pl.onclickTing &&  pl.onclickTingChuPai) {
        gt.setPlaySubstate(ZhuojiGameConstant.GAME_TABLE_SUB_STATE_AUTO_MO_CARD);
    }

    let msg = {
        uid : pl.getUid(),
        operation : ZhuojiGameConstant.MAHJONG_OPERTAION_CHU,
    };

    pl.push("onZhuojiNotifyActive", msg);
    if (pl.isRobot === 1) {
        // ZhuojiRobotService.handleRobotMsg(new RobotMsg(gt, pl, msg));
        pomelo.app.get("zhuojiRobotService").pushMsg(new RobotMsg(gt, pl, msg, DateUtil.nowMS()));
    }
};

function wait_player_gang(gt, waiting, pl) {
    // let hasGrabCard = pl.grabCard === 0 ? 0 : 1;
    // gt.broadCast("onZhuojiTip", {uid : pl.getUid(), hasGrabCard : hasGrabCard});

    let result = ZhuojiMahjongProcessor.checkGang(gt, pl.getCardGrab(), pl, true, pl);
    let gangList = result == null ? [] : result.gangList;
    pl.gangList = gangList;

    // 等待玩家出牌
    let gang = new MahjongCheckResult();
    gang.uid = waiting.uid;
    gang.operation = ZhuojiGameConstant.MAHJONG_OPERTAION_GANG;
    //TODO fanResult
    // gang.addFanResult(getWaitFan(gt));
    gang.gangList = gangList;
    gt.setWaitingPlayerOperate(gang);

    let msg = {
        uid : pl.getUid(),
        operation : ZhuojiGameConstant.MAHJONG_OPERTAION_GANG,
        gangList : gangList
    };

    pl.push("onZhuojiNotifyActive", msg);
    if (pl.isRobot === 1) {
        // ZhuojiRobotService.handleRobotMsg(new RobotMsg(gt, pl, msg));
        pomelo.app.get("zhuojiRobotService").pushMsg(new RobotMsg(gt, pl, msg, DateUtil.nowMS()));
    }
};

// function getWaitFan(gt) {
//
// };

function printOpeartionLog(operation, card, player, table) {
    let s = `operation = null`;
    if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_CHU) {
        s = `operation = chu`;
    } else if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_PENG) {
        s = `operation = peng`;
    } else if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_GANG) {
        s = `operation = gang`;
    } else if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_HU) {
        s = `operation = hu`;
    } else if (operation === ZhuojiGameConstant.MAHJONG_OPERTAION_CANCEL) {
        s = `operation = cancel`;
    }
    logger.info(s + `, card = 0x${card.toString(16)}, playerUid = ${player.getUid()}, table = ${table.getTid()}, cardsInHand = `
                  + player.cardsInHandToString() + `, cardsDown = ` + player.cardsDownToString());
};

module.exports = ZhuojiGameLogicProcessor;